/*
exampleFui:  example of usage of Fui framework.
    Copyright (C) 2012  Jorge E. A. Atala
                        FuDePAN
    This file is part of the Fui Proyect.

    Fui is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Fui is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Fui.  If not, see <http://www.gnu.org/licenses/>.
*/

#define IMPL_CPU

#include <cstdlib>
#include <iostream>
#include "../fui.h"
#include <functional>


struct PlusOperation : public fui::binary_function<int, int, int>
{
    __host__ __device__
    int operator()(int a, int b)
    {
        return a + b;
    }
};

struct PlusWithConstOperation : public fui::unary_function<int, int>
{
    int x;

    __host__ __device__
    PlusWithConstOperation(int factor)
    {
        x = factor;
    }

    __host__ __device__
    int operator()(int a)const
    {
        return a + x;
    }
};

struct MoreThanOne : public fui::unary_function<int, bool>
{
    __host__ __device__
    bool operator()(int a)const
    {
        return (a > 1);
    }
};

int main()
{
    //create a vector of ints with the range 1 to 45,
    //then make a reduction (plus all elements)
    fui::counting_iterator<int>::type first(1);
    fui::counting_iterator<int>::type last = first + 44;

    std::cout << fui::reduce(first, last) << std::endl;

    //Show the item in first vector, whose position is pointed for the value
    //of the item form the second vector
    fui::device_vector<int>::type d_vecPerm(45, 10);
    //d_vecPerm[44] = 15;
    //fui::Device_vector<int> d_vecPerm(45,10);
    fui::permutation_iterator < fui::counting_iterator<int>::type,
        fui::device_vector<int>::type::iterator >::type
        permFirst(first, d_vecPerm.begin());
    fui::permutation_iterator < fui::counting_iterator<int>::type,
        fui::device_vector<int>::type::iterator >::type
        permLast = permFirst + 44;


    std::cout << *permLast << std::endl;

    //show the value of apply a unaryFunction to the element pointed for the iterator
    fui::transform_iterator < PlusWithConstOperation,
        fui::counting_iterator<int>::type
        >::type
        transformFirst(first, PlusWithConstOperation(1));
    fui::transform_iterator < PlusWithConstOperation,
        fui::counting_iterator<int>::type
        >::type
        transformLast(last, PlusWithConstOperation(1));

    std::cout << fui::reduce(transformFirst, transformLast) << std::endl;


    //from two vectors fills a third one with the result of apply a binary operation
    //between the elements in the same position from the inputs vectors
    fui::device_vector<int>::type d_vec1(45, 10);
    fui::device_vector<int>::type d_vec2(45, 10);
    fui::device_vector<int>::type d_vec3(45);
    fui::transform(d_vec1.begin(), d_vec1.end(), d_vec2.begin(), d_vec3.begin(), PlusOperation());

    std::cout << d_vec3[0] << std::endl;

    //fui::_for examples on the three variants
    fui::device_vector<int>::type vec1(4, 2);
    fui::device_vector<int>::type vec2(4, 3);
    fui::device_vector<int>::type result1(4);
    fui::device_vector<int>::type result2(16);
    fui::device_vector<int>::type result3(6);

    fui::For<fui::SideBySidePolicy> loopS;
    loopS.run(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result1.begin(), PlusOperation());
    std::cout << fui::reduce(result1.begin(), result1.end()) << std::endl;

    fui::For<fui::OneToAllPolicy> loopA;
    loopA.run(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result2.begin(), PlusOperation());
    std::cout << fui::reduce(result2.begin(), result2.end()) << std::endl;

    fui::For<fui::OneToLeftPolicy> loopL;
    loopL.run(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result3.begin(), PlusOperation());
    std::cout << fui::reduce(result3.begin(), result3.end()) << std::endl;

    loopL.combine(vec1.begin(), vec1.begin() + 5, result3.begin(), PlusOperation(), 5);


    //find algorithms
    fui::device_vector<int>::type::iterator it = fui::find(vec1.begin(), vec1.end(), 2);
    std::cout << *it << std::endl;
    it = fui::find_if(vec1.begin(), vec1.end(), MoreThanOne());
    std::cout << *it << std::endl;

    fui::pair < fui::device_vector<int>::type::iterator,
        fui::device_vector<int>::type::iterator >
        pair1 = fui::mismatch(vec1.begin(), vec1.end(), vec2.begin());
    std::cout << *(pair1.first) << std::endl;

    std::cout << fui::make_pair(8, 12).first << std::endl;

    return 0;

}
